En guide till SQLAlchemy sessionshantering i Python, med fokus pÄ robust transaktionshantering för att sÀkerstÀlla dataintegritet och konsistens i applikationer.
Python SQLAlchemy Sessionshantering: BemÀstra Transaktionshantering för Data Integritet
SQLAlchemy Àr ett kraftfullt och flexibelt Python-bibliotek som tillhandahÄller en omfattande verktygslÄda för att interagera med databaser. KÀrnan i SQLAlchemy Àr konceptet med en session, som fungerar som ett förberedande omrÄde för alla operationer du utför pÄ din databas. Korrekt sessions- och transaktionshantering Àr avgörande för att upprÀtthÄlla dataintegritet och sÀkerstÀlla konsekvent databasbeteende, sÀrskilt i komplexa applikationer som hanterar samtidiga förfrÄgningar.
FörstÄ SQLAlchemy-sessioner
En SQLAlchemy Session representerar en arbetsenhet, en konversation med databasen. Den spÄrar Àndringar som gjorts i objekt, vilket gör att du kan spara dem till databasen som en enda atomÀr operation. TÀnk pÄ det som en arbetsyta dÀr du gör Àndringar i data innan du officiellt sparar dem. Utan en vÀlskött session riskerar du datainkonsekvenser och potentiell korruption.
Skapa en session
Innan du kan börja interagera med din databas mÄste du skapa en session. Detta innebÀr att först upprÀtta en anslutning till databasen med hjÀlp av SQLAlchemy:s motor (engine).
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# DatabasanslutningsstrÀng
db_url = 'sqlite:///:memory:' # ErsÀtt med din databas-URL (t.ex. PostgreSQL, MySQL)
# Skapa en motor (engine)
engine = create_engine(db_url, echo=False) # echo=True för att se den genererade SQL:en
# Definiera en bas för deklarativa modeller
Base = declarative_base()
# Definiera en enkel modell
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
def __repr__(self):
return f""
# Skapa tabellen i databasen
Base.metadata.create_all(engine)
# Skapa en sessionsklass
Session = sessionmaker(bind=engine)
# Instantiera en session
session = Session()
I detta exempel:
- Vi importerar nödvÀndiga SQLAlchemy-moduler.
- Vi definierar en databasanslutningsstrÀng (`db_url`). Detta exempel anvÀnder en SQLite-databas i minnet för enkelhetens skull, men du skulle ersÀtta den med en anslutningsstrÀng som Àr lÀmplig för ditt databassystem (t.ex. PostgreSQL, MySQL). Det specifika formatet varierar beroende pÄ vilken databasmotor och drivrutin du anvÀnder. Konsultera SQLAlchemy-dokumentationen och din databasleverantörs dokumentation för korrekt anslutningsstrÀngsformat.
- Vi skapar en `engine` med hjÀlp av `create_engine()`. Motorn ansvarar för att hantera anslutningspoolen och kommunikationen med databasen. Parametern `echo=True` kan vara till hjÀlp vid felsökning, eftersom den skriver ut de genererade SQL-satserna till konsolen.
- Vi definierar en basklass (`Base`) med hjÀlp av `declarative_base()`. Denna anvÀnds som basklass för alla vÄra SQLAlchemy-modeller.
- Vi definierar en `User`-modell och mappar den till en databastabell med namnet `users`.
- Vi skapar tabellen i databasen med `Base.metadata.create_all(engine)`.
- Vi skapar en sessionsklass med hjÀlp av `sessionmaker(bind=engine)`. Detta konfigurerar sessionsklassen att anvÀnda den angivna motorn.
- Slutligen instansierar vi en session med `Session()`.
FörstÄ Transaktioner
En transaktion Àr en sekvens av databasoperationer som behandlas som en enda logisk arbetsenhet. Transaktioner följer ACID-egenskaperna:
- Atomicitet: Alla operationer i transaktionen lyckas antingen helt eller misslyckas helt. Om nÄgon del av transaktionen misslyckas, rullas hela transaktionen tillbaka.
- Konsistens: Transaktionen mÄste upprÀtthÄlla databasen i ett giltigt tillstÄnd. Den fÄr inte bryta nÄgra databasbegrÀnsningar eller regler.
- Isolering: Samtidiga transaktioner Ă€r isolerade frĂ„n varandra. Ăndringar som görs av en transaktion Ă€r inte synliga för andra transaktioner förrĂ€n den första transaktionen har committats.
- Varaktighet: NÀr en transaktion har committats Àr dess Àndringar permanenta och kommer att överleva Àven systemfel.
SQLAlchemy tillhandahÄller mekanismer för att hantera transaktioner, vilket sÀkerstÀller att dessa ACID-egenskaper upprÀtthÄlls.
GrundlÀggande Transaktionshantering
De vanligaste transaktionsoperationerna Àr commit och rollback.
Committa Transaktioner
NÀr alla operationer inom en transaktion har slutförts framgÄngsrikt, committar du transaktionen. Detta sparar Àndringarna till databasen.
try:
# LÀgg till en ny anvÀndare
new_user = User(name='Alice Smith', email='alice.smith@example.com')
session.add(new_user)
# Committa transaktionen
session.commit()
print("Transaktionen committades framgÄngsrikt!")
except Exception as e:
# Hantera undantag
print(f"Ett fel uppstod: {e}")
session.rollback()
print("Transaktionen rullades tillbaka.")
finally:
session.close()
I detta exempel:
- Vi lÀgger till ett nytt `User`-objekt till sessionen.
- Vi anropar `session.commit()` för att spara Àndringarna till databasen.
- Vi omsluter koden i ett `try...except...finally`-block för att hantera potentiella undantag.
- Om ett undantag uppstÄr, anropar vi `session.rollback()` för att Ängra eventuella Àndringar som gjorts under transaktionen.
- Vi anropar alltid `session.close()` i `finally`-blocket för att slÀppa sessionen och returnera anslutningen till anslutningspoolen. Detta Àr avgörande för att undvika resurslÀckage. Att misslyckas med att stÀnga sessioner kan leda till anslutningsutarmning och applikationsinstabilitet.
Rulla tillbaka Transaktioner
Om nÄgot fel uppstÄr under en transaktion, eller om du bestÀmmer dig för att Àndringarna inte ska sparas, rullar du tillbaka transaktionen. Detta ÄterstÀller databasen till dess tillstÄnd innan transaktionen pÄbörjades.
try:
# LÀgg till en anvÀndare med ogiltig e-post (exempel för att tvinga fram en rollback)
invalid_user = User(name='Bob Johnson', email='invalid-email')
session.add(invalid_user)
# Committen kommer att misslyckas om e-postadressen inte valideras pÄ databasnivÄ
session.commit()
print("Transaktionen committades.")
except Exception as e:
print(f"Ett fel uppstod: {e}")
session.rollback()
print("Transaktionen rullades tillbaka framgÄngsrikt.")
finally:
session.close()
I detta exempel, om tillÀgget av `invalid_user` utlöser ett undantag (t.ex. pÄ grund av en databasbegrÀnsning), kommer anropet till `session.rollback()` att Ängra det försöka infogandet, och lÀmna databasen oförÀndrad.
Avancerad Transaktionshantering
AnvÀnda `with`-satsen för TransaktionsomfÄng
Ett mer Pythoniskt och robust sÀtt att hantera transaktioner Àr att anvÀnda `with`-satsen. Detta sÀkerstÀller att sessionen stÀngs korrekt, Àven om undantag uppstÄr.
from contextlib import contextmanager
@contextmanager
def session_scope():
"""TillhandahÄller ett transaktionellt omfÄng kring en serie operationer."""
session = Session()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
# AnvÀndning:
with session_scope() as session:
new_user = User(name='Charlie Brown', email='charlie.brown@example.com')
session.add(new_user)
# Operationer inom 'with'-blocket
# Om inga undantag uppstÄr, committas transaktionen automatiskt.
# Om ett undantag uppstÄr, rullas transaktionen tillbaka automatiskt.
print("AnvÀndare tillagd.")
print("Transaktion slutförd (committad eller tillbakarullad).")
Funktionen `session_scope` Àr en kontexthanterare. NÀr du gÄr in i `with`-blocket skapas en ny session. NÀr du lÀmnar `with`-blocket blir sessionen antingen committad (om inga undantag uppstod) eller tillbakarullad (om ett undantag uppstod). Sessionen stÀngs alltid i `finally`-blocket.
NĂ€sta Transaktioner (Savepoints)
SQLAlchemy stöder nÀsta transaktioner med hjÀlp av savepoints. En savepoint gör att du kan rulla tillbaka till en specifik punkt inom en större transaktion, utan att pÄverka hela transaktionen.
try:
with session_scope() as session:
user1 = User(name='David Lee', email='david.lee@example.com')
session.add(user1)
session.flush() # Skicka Àndringar till databasen men committa inte Ànnu
# Skapa en savepoint
savepoint = session.begin_nested()
try:
user2 = User(name='Eve Wilson', email='eve.wilson@example.com')
session.add(user2)
session.flush()
# Simulera ett fel
raise ValueError("Simulerat fel under nÀsta transaktion")
except Exception as e:
print(f"Fel i nÀsta transaktion: {e}")
savepoint.rollback()
print("NĂ€sta transaktion rullades tillbaka till savepoint.")
# FortsÀtt med den yttre transaktionen, user1 kommer fortfarande att lÀggas till
user3 = User(name='Frank Miller', email='frank.miller@example.com')
session.add(user3)
except Exception as e:
print(f"Fel i yttre transaktion: {e}")
# Commit kommer att committa user1 och user3, men inte user2 pÄ grund av den nÀsta tillbakarullningen
try:
with session_scope() as session:
# Verifiera att endast user1 och user3 finns
users = session.query(User).all()
for user in users:
print(user)
except Exception as e:
print(f"OvÀntat undantag: {e}") # Bör inte hÀnda
I detta exempel:
- Vi startar en yttre transaktion med `session_scope()`.
- Vi lÀgger till `user1` i sessionen och flushar Àndringarna till databasen. `flush()` skickar Àndringarna till databasservern men committar dem *inte*. Den lÄter dig se om Àndringarna Àr giltiga (t.ex. inga begrÀnsningsövertrÀdelser) innan du committar hela transaktionen.
- Vi skapar en savepoint med `session.begin_nested()`.
- Inom den nÀsta transaktionen lÀgger vi till `user2` och simulerar ett fel.
- Vi rullar tillbaka den nÀsta transaktionen till savepointen med `savepoint.rollback()`. Detta Ängrar endast Àndringarna som gjorts inom den nÀsta transaktionen (d.v.s. tillÀgget av `user2`).
- Vi fortsÀtter med den yttre transaktionen och lÀgger till `user3`.
- Den yttre transaktionen committas, vilket sparar `user1` och `user3` till databasen, medan `user2` kasseras pÄ grund av savepoint-tillbakarullningen.
Kontrollera IsoleringsnivÄer
IsoleringsnivÄer definierar graden i vilken samtidiga transaktioner Àr isolerade frÄn varandra. Högre isoleringsnivÄer ger större datakonsistens men kan minska samtidighet och prestanda. SQLAlchemy lÄter dig kontrollera isoleringsnivÄn för dina transaktioner.
Vanliga isoleringsnivÄer inkluderar:
- Read Uncommitted: Den lÀgsta isoleringsnivÄn. Transaktioner kan se ocommittade Àndringar gjorda av andra transaktioner. Detta kan leda till dirty reads (smutsiga lÀsningar).
- Read Committed: Transaktioner kan endast se committade Àndringar gjorda av andra transaktioner. Detta förhindrar dirty reads men kan leda till non-repeatable reads (icke-repeterbara lÀsningar) och phantom reads (fantominlÀsningar).
- Repeatable Read: Transaktioner kan se samma data under hela transaktionen, Àven om andra transaktioner modifierar den. Detta förhindrar dirty reads och non-repeatable reads men kan leda till phantom reads.
- Serializable: Den högsta isoleringsnivÄn. Transaktioner Àr helt isolerade frÄn varandra. Detta förhindrar dirty reads, non-repeatable reads och phantom reads men kan avsevÀrt minska samtidigheten.
StandardisoleringsnivÄn beror pÄ databassystemet. Du kan stÀlla in isoleringsnivÄn nÀr du skapar motorn eller nÀr du pÄbörjar en transaktion.
Exempel (PostgreSQL):
from sqlalchemy.dialects.postgresql import dialect
# StÀll in isoleringsnivÄ nÀr motorn skapas
engine = create_engine('postgresql://user:password@host:port/database',
connect_args={'options': '-c statement_timeout=1000'} # Exempel pÄ timeout
)
# StÀll in isoleringsnivÄn nÀr en transaktion pÄbörjas (databasspecifik)
# För PostgreSQL rekommenderas det att stÀlla in den pÄ anslutningen, inte motorn.
from sqlalchemy import event
from sqlalchemy.pool import Pool
@event.listens_for(Pool, "connect")
def set_isolation_level(dbapi_connection, connection_record):
existing_autocommit = dbapi_connection.autocommit
dbapi_connection.autocommit = True
cursor = dbapi_connection.cursor()
cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE")
dbapi_connection.autocommit = existing_autocommit
cursor.close()
# DÀrefter kommer transaktioner skapade via SQLAlchemy att anvÀnda den konfigurerade isoleringsnivÄn.
Viktigt: Metoden för att stÀlla in isoleringsnivÄer Àr databasspecifik. Se din databasdokumentation för korrekt syntax. Att stÀlla in isoleringsnivÄer felaktigt kan leda till ovÀntat beteende eller fel.
Hantering av Samtidighet
NÀr flera anvÀndare eller processer kommer Ät samma data samtidigt Àr det avgörande att hantera samtidighet korrekt för att förhindra datakorruption och sÀkerstÀlla datakonsistens. SQLAlchemy tillhandahÄller flera mekanismer för att hantera samtidighet, inklusive optimistisk lÄsning och pessimistisk lÄsning.
Optimistisk LÄsning
Optimistisk lÄsning antar att konflikter Àr sÀllsynta. Den kontrollerar om Àndringar har gjorts av andra transaktioner innan en transaktion committas. Om en konflikt upptÀcks rullas transaktionen tillbaka.
För att implementera optimistisk lÄsning lÀgger du vanligtvis till en versionskolumn i din tabell. Denna kolumn ökas automatiskt varje gÄng raden uppdateras.
from sqlalchemy import Column, Integer, String, Integer
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = Column(String)
content = Column(String)
version = Column(Integer, nullable=False, default=1)
def __repr__(self):
return f""
# Inuti try-catch-blocket
def update_article(session, article_id, new_content):
article = session.query(Article).filter_by(id=article_id).first()
if article is None:
raise ValueError("Artikel hittades inte")
original_version = article.version
# Uppdatera innehÄllet och öka versionen
article.content = new_content
article.version += 1
# Försök att uppdatera, kontrollera versionskolumnen i WHERE-satsen
rows_affected = session.query(Article).filter(
Article.id == article_id,
Article.version == original_version
).update({
Article.content: new_content,
Article.version: article.version
}, synchronize_session=False)
if rows_affected == 0:
session.rollback()
raise ValueError("Konflikt: Artikeln har uppdaterats av en annan transaktion.")
session.commit()
I detta exempel:
- Vi lÀgger till en `version`-kolumn till `Article`-modellen.
- Innan vi uppdaterar artikeln, lagrar vi det aktuella versionsnumret.
- I `UPDATE`-satsen inkluderar vi en `WHERE`-klausul som kontrollerar om versionskolumnen fortfarande Àr lika med det lagrade versionsnumret. `synchronize_session=False` förhindrar SQLAlchemy frÄn att ladda det uppdaterade objektet igen; vi hanterar versionshanteringen explicit.
- Om versionskolumnen har Àndrats av en annan transaktion, kommer `UPDATE`-satsen inte att pÄverka nÄgra rader (rows_affected blir 0), och vi utlöser ett undantag.
- Vi rullar tillbaka transaktionen och meddelar anvÀndaren att en konflikt har uppstÄtt.
Pessimistisk LÄsning
Pessimistisk lÄsning antar att konflikter Àr troliga. Den förvÀrvar ett lÄs pÄ en rad eller tabell innan den modifierar den. Detta förhindrar andra transaktioner frÄn att modifiera datan tills lÄset slÀpps.
SQLAlchemy tillhandahÄller flera funktioner för att förvÀrva lÄs, sÄsom `with_for_update()`.
# Exempel med PostgreSQL
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
# DatabasinstÀllning (ersÀtt med din faktiska databas-URL)
db_url = 'postgresql://user:password@host:port/database'
engine = create_engine(db_url, echo=False) # StÀll in echo till true om du vill se den genererade SQL:en
Base = declarative_base()
class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(Integer)
def __repr__(self):
return f"- "
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
# Funktion för att uppdatera objektet (inom ett try/except-block)
def update_item_value(session, item_id, new_value):
# FörvÀrva ett pessimistiskt lÄs pÄ objektet
item = session.query(Item).filter(Item.id == item_id).with_for_update().first()
if item is None:
raise ValueError("Objekt hittades inte")
# Uppdatera objektets vÀrde
item.value = new_value
session.commit()
return True
I detta exempel:
- Vi anvÀnder `with_for_update()` för att förvÀrva ett lÄs pÄ `Item`-raden innan vi uppdaterar den. Detta förhindrar andra transaktioner frÄn att modifiera raden tills den aktuella transaktionen committas eller rullas tillbaka. Funktionen `with_for_update()` Àr databasspecifik; konsultera din databasdokumentation för detaljer. Vissa databaser kan ha olika lÄsmekanismer eller syntax.
Viktigt: Pessimistisk lÄsning kan minska samtidigheten och prestandan, sÄ anvÀnd den endast nÀr det Àr nödvÀndigt.
BÀsta praxis för undantagshantering
Korrekt undantagshantering Àr avgörande för att sÀkerstÀlla dataintegritet och förhindra applikationskrascher. Omslut alltid dina databasoperationer i `try...except`-block och hantera undantag pÄ lÀmpligt sÀtt.
HÀr Àr nÄgra bÀsta praxis för undantagshantering:
- FÄnga specifika undantag: Undvik att fÄnga generiska undantag som `Exception`. FÄnga specifika undantag som `sqlalchemy.exc.IntegrityError` eller `sqlalchemy.exc.OperationalError` för att hantera olika typer av fel pÄ olika sÀtt.
- Rulla tillbaka transaktioner: Rulla alltid tillbaka transaktionen om ett undantag uppstÄr.
- Logga undantag: Logga undantag för att hjÀlpa till att diagnostisera och ÄtgÀrda problem. Inkludera sÄ mycket kontext som möjligt i dina loggar (t.ex. anvÀndar-ID, indata, tidsstÀmpel).
- Vidarebefordra undantag nÀr det Àr lÀmpligt: Om du inte kan hantera ett undantag, vidarebefordra det för att lÄta en hanterare pÄ högre nivÄ hantera det.
- Rensa resurser: StÀng alltid sessionen och slÀpp alla andra resurser i ett `finally`-block.
import logging
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.exc import IntegrityError, OperationalError
# Konfigurera loggning
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# DatabasinstÀllning (ersÀtt med din faktiska databas-URL)
db_url = 'postgresql://user:password@host:port/database'
engine = create_engine(db_url, echo=False)
Base = declarative_base()
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String)
price = Column(Integer)
def __repr__(self):
return f""
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
# Funktion för att lÀgga till en produkt
def add_product(session, name, price):
try:
new_product = Product(name=name, price=price)
session.add(new_product)
session.commit()
logging.info(f"Produkt '{name}' lades till framgÄngsrikt.")
return True
except IntegrityError as e:
session.rollback()
logging.error(f"IntegrityError: {e}")
# Hantera databasbegrÀnsningsövertrÀdelser (t.ex. dubblettnamn)
return False
except OperationalError as e:
session.rollback()
logging.error(f"OperationalError: {e}")
# Hantera anslutningsfel eller andra operativa problem
return False
except Exception as e:
session.rollback()
logging.exception(f"Ett ovÀntat fel uppstod: {e}")
# Hantera andra ovÀntade fel
return False
finally:
session.close()
I detta exempel:
- Vi konfigurerar loggning för att registrera hÀndelser under processen.
- Vi fÄngar specifika undantag som `IntegrityError` (för begrÀnsningsövertrÀdelser) och `OperationalError` (för anslutningsfel).
- Vi rullar tillbaka transaktionen i `except`-blocken.
- Vi loggar undantagen med hjÀlp av `logging`-modulen. Metoden `logging.exception()` inkluderar automatiskt stackspÄrningen i loggmeddelandet.
- Vi vidarebefordrar undantaget om vi inte kan hantera det.
- Vi stÀnger sessionen i `finally`-blocket.
Databaskopplingspoolning
SQLAlchemy anvÀnder anslutningspoolning för att effektivt hantera databasanslutningar. En anslutningspool upprÀtthÄller en uppsÀttning öppna anslutningar till databasen, vilket gör att applikationer kan ÄteranvÀnda befintliga anslutningar istÀllet för att skapa nya för varje begÀran. Detta kan avsevÀrt förbÀttra prestanda, sÀrskilt i applikationer som hanterar ett stort antal samtidiga begÀranden.
SQLAlchemy:s `create_engine()`-funktion skapar automatiskt en anslutningspool. Du kan konfigurera anslutningspoolen genom att skicka argument till `create_engine()`.
Vanliga anslutningspoolparametrar inkluderar:
- pool_size: Det maximala antalet anslutningar i poolen.
- max_overflow: Antalet anslutningar som kan skapas utöver `pool_size`.
- pool_recycle: Antalet sekunder efter vilka en anslutning Ätervinns.
- pool_timeout: Antalet sekunder att vÀnta pÄ att en anslutning ska bli tillgÀnglig.
engine = create_engine('postgresql://user:password@host:port/database',
pool_size=5, # Maximal poolstorlek
max_overflow=10, # Maximalt överflöd
pool_recycle=3600, # Ă
tervinn anslutningar efter 1 timme
pool_timeout=30
)
Viktigt: VÀlj lÀmpliga instÀllningar för anslutningspoolen baserat pÄ din applikations behov och din databasservers kapacitet. En dÄligt konfigurerad anslutningspool kan leda till prestandaproblem eller anslutningsutarmning.
Asynkrona Transaktioner (Async SQLAlchemy)
För moderna applikationer som krÀver hög samtidighet, sÀrskilt de som Àr byggda med asynkrona ramverk som FastAPI eller AsyncIO, erbjuder SQLAlchemy en asynkron version som kallas Async SQLAlchemy.
Async SQLAlchemy tillhandahÄller asynkrona versioner av SQLAlchemy:s kÀrnkomponenter, vilket gör att du kan utföra databasoperationer utan att blockera hÀndelseloopen. Detta kan avsevÀrt förbÀttra prestandan och skalbarheten i dina applikationer.
HÀr Àr ett grundlÀggande exempel pÄ att anvÀnda Async SQLAlchemy:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String
import asyncio
# DatabasinstÀllning (ersÀtt med din faktiska databas-URL)
db_url = 'postgresql+asyncpg://user:password@host:port/database'
engine = create_async_engine(db_url, echo=False)
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
def __repr__(self):
return f""
async def create_db_and_tables():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def add_user(name, email):
async with AsyncSession(engine) as session:
new_user = User(name=name, email=email)
session.add(new_user)
await session.commit()
async def main():
await create_db_and_tables()
await add_user("Async User", "async.user@example.com")
if __name__ == "__main__":
asyncio.run(main())
Viktiga skillnader frÄn synkron SQLAlchemy:
- `create_async_engine` anvÀnds istÀllet för `create_engine`.
- `AsyncSession` anvÀnds istÀllet för `Session`.
- Alla databasoperationer Àr asynkrona och mÄste avvaktas med `await`.
- Asynkrona databasdrivrutiner (t.ex. `asyncpg` för PostgreSQL) mÄste anvÀndas.
Viktigt: Async SQLAlchemy krÀver en databasdrivrutin som stöder asynkrona operationer. Se till att du har rÀtt drivrutin installerad och konfigurerad.
Slutsats
Att bemÀstra SQLAlchemy:s sessions- och transaktionshantering Àr avgörande för att bygga robusta och pÄlitliga Python-applikationer som interagerar med databaser. Genom att förstÄ begreppen sessioner, transaktioner, isoleringsnivÄer och samtidighet, och genom att följa bÀsta praxis för undantagshantering och anslutningspoolning, kan du sÀkerstÀlla dataintegritet och optimera prestandan för dina applikationer.
Oavsett om du bygger en liten webbapplikation eller ett storskaligt företagssystem, tillhandahÄller SQLAlchemy de verktyg du behöver för att hantera dina databasinteraktioner effektivt. Kom ihÄg att alltid prioritera dataintegritet och hantera potentiella fel pÄ ett elegant sÀtt för att sÀkerstÀlla tillförlitligheten i dina applikationer.
ĂvervĂ€g att utforska avancerade Ă€mnen som:
- Two-Phase Commit (2PC): För transaktioner som strÀcker sig över flera databaser.
- Sharding: För att distribuera data över flera databasservrar.
- Databasomvandlingar: AnvÀnda verktyg som Alembic för att hantera Àndringar i databasschemat.